/*
 * GameComponent.h
 *
 *  Created on: May 12, 2012
 *      Author: mklingen
 */

#ifndef GAMECOMPONENT_H
#define GAMECOMPONENT_H

#include <Eigen/Core>
#include <Eigen/Dense>
#include <map>
#include <vector>
#include "GameComponentManager.h"
#include "GameComponentDefs.h"
#include <QKeyEvent>
#include <QMouseEvent>

namespace kling
{

    // Each component has a global and local ID. Global ID is unique for all components.
    // local ID is unique among the children of a parent.

    class GameComponentManager;

    // Class for dealing with a scene graph, handles updating, drawing, etc.
    class GameComponent
    {
        public:
            GameComponent(GameComponentManager* componentManager = NULL, std::string name = "UNINITIALIZED", GlobalComponentID globalID = INVALID_GLOBAL_ID);
            virtual ~GameComponent();


            // Sets this component to be deleted, and does any other
            // special effects that the game requires.
            virtual void Destroy();

            // Allocates memory, etc. Sets IsInitialized to true.
            virtual void Initialize();

            // Logic update for this component. DeltaTimeSeconds is the time in seconds
            // since the last frame.
            virtual void Update(double deltaTimeSeconds);

            // Draws this component to the screen. DeltaTimeSeconds is the time in seconds
            // since the last frame.
            virtual void Render(double deltaTimeSeconds);

            // Gets the pose relative to the parent.
            inline Eigen::Affine2d& GetLocalPose() { return m_localPose; }

            // Gets the pose relative to the wrold.
            inline Eigen::Affine2d& GetGlobalPose() { return m_globalPose; }

            // Gets the pose relative to the parent.
            inline void SetLocalPose(Eigen::Affine2d& localPose) { m_localPose = localPose; }

            // Gets the component which owns this component.
            inline GameComponent* GetParent() { return m_parent; }

            // Sets the component which owns this component to the given component.
            inline void SetParent(GameComponent* parent) { m_parent = parent; }

            inline const std::string& GetName() const { return m_name; }
            inline void SetName(std::string name) { m_name = name; }

            inline const GlobalComponentID& GetGlobalID() const { return m_globalID; }
            inline void SetGlobalID(GlobalComponentID componentID) { m_globalID = componentID; }

            inline const LocalComponentID& GetLocalID() const { return m_localID; }
            inline void SetLocalID(LocalComponentID componentID) { m_localID = componentID; }

            inline std::map<LocalComponentID, GameComponent*>& GetChildren() { return m_children; }

            // Gets the child with the given local ID, or returns NULL if no such child
            // exists.
            GameComponent* GetChildByLocalID(LocalComponentID localID);

            // Gets the child with the given global ID, or returns NULL if no such child
            // exists.
            GameComponent* GetChildByGlobalID(GlobalComponentID globalID);

            // Gets the first child that has the given name. If no such child exists, return null.
            GameComponent* GetFirstChildByName(const std::string& name);

            // Gets a list of all children with the given name.
            std::vector<GameComponent*> GetAllChildrenWithName(const std::string& name);

            // Returns true if there is a child with the given local ID.
            bool ContainsChildWithLocalID(LocalComponentID localID);

            // Returns true if there is a child with the given global ID.
            bool ContainsChildWithGlobalID(GlobalComponentID globalID);

            // Given a component, adds it as a child with the given local ID.
            // Returns false if the component is already a child, or if the local ID is not unique.
            // If localID = INVALID_LOCAL_ID, generates a new local ID for the component.
            bool AddChild(GameComponent* child, LocalComponentID localID = INVALID_LOCAL_ID);

            // Attempts to remove the given child, and returns false if the child
            // is not a member of this parent. If destroy is true, destroys the child
            // as well.
            bool RemoveChild(GameComponent* child, bool destroy = false);
            bool RemoveChildByLocalID(LocalComponentID localID, bool destroy = false);
            bool RemoveChildByGlobalID(GlobalComponentID globalID, bool destroy = false);
            bool RemoveFirstChildWithName(const std::string& name, bool destroy = false);
            bool RemoveAllChildrenWithName(const std::string& name, bool destroy = false);



            // Increments the maximum local ID, and returns it (ensuring unique Local IDs)
            inline LocalComponentID GetNextLocalID() { return ++m_maxLocalID; }

            inline bool IsInitialized() { return m_isInitialized; }
            inline bool IsDestroyed() { return m_isDestroyed; }

            void SetLocalRotation(double angle);
            void SetGlobalRotation(double angle);
            double GetLocalRotaiton() { return m_localRotation; }
            double GetGlobalRotation() { return m_globalRotation; }

            inline void SetLocalPosition(Eigen::Vector2d position) { m_localPose.translation() = position; }
            inline Eigen::Vector2d GetLocalPosition() { return m_localPose.translation(); }
            inline void SetGlobalPosition(Eigen::Vector2d position) { m_globalPose.translation() = position; }
            inline Eigen::Vector2d GetGlobalPosition() { return m_globalPose.translation(); }


            inline GameComponentManager* GetComponentManager() { return m_componentManager; }

            // Returns the first child with the type given. If no child is found
            // with that type, returns NULL
            template <class ComponentType> ComponentType* GetFirstChildByType()
            {
                for(std::map<LocalComponentID, GameComponent*>::iterator it = m_children.begin(); it != m_children.end(); it++)
                {
                    ComponentType* toReturn = dynamic_cast<ComponentType*>(it->second);
                    if(toReturn != NULL)
                    {
                        return toReturn;
                    }
                }

                return NULL;
            }


            // Returns a vector of all the children with the given type
            template <class ComponentType> std::vector<ComponentType*> GetAllChildrenByType()
            {
                std::vector<ComponentType*> toReturn;
                for(std::map<LocalComponentID, GameComponent*>::iterator it = m_children.begin(); it != m_children.end(); it++)
                {
                    ComponentType* child = dynamic_cast<ComponentType*>(it->second);
                    if(child != NULL)
                    {
                        toReturn.push_back(child);
                    }
                }

                return toReturn;
            }

            // Updates all of the global poses of the children of this component (and their children too)
            void ComputeGlobalPoseRecursive();

            virtual void HandleKeyEvent(QKeyEvent* keyEvent);
            virtual void HandleMouseEvent(QMouseEvent* mouseEvent);

        protected:
            // Pose relative to the parent component
            Eigen::Affine2d m_localPose;

            // Pose relative to the world.
            Eigen::Affine2d m_globalPose;

            // Component which owns this component.
            GameComponent* m_parent;

            // Unique ID of this component with respect to all components.
            GlobalComponentID m_globalID;

            // Unique ID of this component with respect to the siblings of this component.
            LocalComponentID m_localID;

            // The name of this component (not unique in any sense)
            std::string m_name;

            // The biggest local ID of any child component.
            size_t m_maxLocalID;

            // True only when Initialize has been called.
            bool m_isInitialized;

            // True only when Destroy has been called.
            bool m_isDestroyed;

            // Pointer to the manager in charge of this component.
            GameComponentManager* m_componentManager;


            double m_localRotation;
            double m_globalRotation;

            // Maps local ID's to children of the component. Note that memory is not supposed to be stored
            // in this map, it is only used for reference. Game components are actually stored in a global
            // map somewhere else.
            std::map<LocalComponentID, GameComponent*> m_children;
    };

} /* namespace kling */

#endif /* GAMECOMPONENT_H_ */
